package org.hyperledger.fabric.sdk3;

import static java.nio.charset.StandardCharsets.UTF_8;

import java.io.File;
import java.io.FileInputStream;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.stream.Collectors;

import org.apache.commons.io.IOUtils;
import org.hyperledger.fabric.sdk.ChaincodeID;
import org.hyperledger.fabric.sdk.ChaincodeResponse.Status;
import org.hyperledger.fabric.sdk.Channel;
import org.hyperledger.fabric.sdk.EventHub;
import org.hyperledger.fabric.sdk.Orderer;
import org.hyperledger.fabric.sdk.Peer;
import org.hyperledger.fabric.sdk.ProposalResponse;
import org.hyperledger.fabric.sdk.TransactionProposalRequest;
import org.hyperledger.fabric.sdk.exception.InvalidArgumentException;
import org.hyperledger.fabric.sdk.exception.TransactionException;
import org.hyperledger.fabric.sdk3.client.CAClient;
import org.hyperledger.fabric.sdk3.client.ChannelClient;
import org.hyperledger.fabric.sdk3.client.FabricClient;
import org.hyperledger.fabric.sdk3.user.UserContext;
import org.slf4j.LoggerFactory;

import cn.hutool.core.exceptions.ExceptionUtil;

public class FabricUtil {

	// 日志记录
	private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(FabricUtil.class);

	private static final byte[] EXPECTED_EVENT_DATA = "!".getBytes(UTF_8);

	private static final String EXPECTED_EVENT_NAME = "event";

	public static void init() throws Exception {

	}

	public static FabricClient getDefaultFabricClient() {
		try {
			Util.cleanUp();
			UserContext adminUser = new UserContext();
			adminUser.setName(Config.ADMIN);
			adminUser.setAffiliation(Config.ORG1);
			adminUser.setMspId(Config.ORG1_MSP);
			File f = new File(Config.CA1_TLSFILE);
			String certficate = new String(IOUtils.toByteArray(new FileInputStream(f)), "UTF-8");
			Properties properties = new Properties();
			properties.put("pemBytes", certficate.getBytes());
			properties.setProperty("pemFile", f.getAbsolutePath());
			properties.setProperty("allowAllHostNames", "true");
			CAClient caclient = new CAClient(Config.CA1NAME, Config.CA_ORG1_URL, properties);
			caclient.setAdminUserContext(adminUser);
			adminUser = caclient.enrollAdminUserTLS("admin", "adminpw");

			return new FabricClient(adminUser);
		} catch (Exception e) {
			throw ExceptionUtil.wrapRuntime(e);
		}
	}

	public static ChannelClient getDefaultChannelClient(FabricClient fabClient) {
		try {
			ChannelClient channelClient = fabClient.createChannelClient(Config.CHANNEL_NAME);
			Channel channel = channelClient.getChannel();
			Properties properties1 = Util.gete2ePro("peer", Config.ORG1_PEER_0);
			Peer peer = fabClient.getInstance().newPeer(Config.ORG1_PEER_0, Config.ORG1_PEER_0_URL, properties1);
			Properties properties2 = Util.gete2ePro("peer", Config.ORG2_PEER_0);
			Peer peer2 = fabClient.getInstance().newPeer(Config.ORG2_PEER_0, Config.ORG2_PEER_0_URL, properties2);
			EventHub eventHub = fabClient.getInstance().newEventHub("eventhub01", Config.grpc + Config.baseUrl + ":7053",
			      properties1);

			Properties orderPro = Util.gete2ePro("orderer", Config.ORDERER_NAME);
			Orderer orderer = fabClient.getInstance().newOrderer(Config.ORDERER_NAME, Config.ORDERER_URL, orderPro);
			channel.addPeer(peer);
			channel.addPeer(peer2);// 背书策略为OR的话不需要添加 peer2
			channel.addEventHub(eventHub);
			channel.addOrderer(orderer);
			channel.initialize();

			return channelClient;
		} catch (InvalidArgumentException | TransactionException e) {
			throw ExceptionUtil.wrapRuntime(e);
		}

	}

	/*
	 * 实现插入数据
	 */
	public static void instertFabcar(FabricClient fabClient, ChannelClient channelClient, String chaincodeName,
	      String fcn, String... args) throws Exception {
		try {
			TransactionProposalRequest request = fabClient.getInstance().newTransactionProposalRequest();
			ChaincodeID ccid = ChaincodeID.newBuilder().setName(chaincodeName).build();
			request.setChaincodeID(ccid);
			request.setFcn(fcn);
			String[] arguments = args;
			request.setArgs(arguments);
			request.setProposalWaitTime(1000);

			Map<String, byte[]> tm2 = new HashMap<>();
			tm2.put("HyperLedgerFabric", "TransactionProposalRequest:JavaSDK".getBytes(UTF_8));
			tm2.put("method", "TransactionProposalRequest".getBytes(UTF_8));
			tm2.put("result", ":)".getBytes(UTF_8));
			tm2.put(EXPECTED_EVENT_NAME, EXPECTED_EVENT_DATA);
			request.setTransientMap(tm2);
			Collection<ProposalResponse> responses = channelClient.sendTransactionProposal(request);
			for (ProposalResponse res : responses) {
				Status status = res.getStatus();
				LOG.info("Invoked on " + Config.CHAINCODE_1_NAME + ". Status - " + status);
				if (!status.equals(Status.SUCCESS)) {
					throw new RuntimeException("执行失败!");
				}
			}
		} catch (Exception e) {
			throw ExceptionUtil.wrapRuntime(e);
		}
	}

	/*
	 * 实现给定的Key查询数据
	 */
	public static String queryFabcar(ChannelClient channelClient, String chaincodeName, String fcn, String... args)
	      throws Exception {
		LOG.info("Querying ...");
		Collection<ProposalResponse> responsesQuery = channelClient.queryByChainCode(chaincodeName, fcn, args);
		return String.join(";", responsesQuery.stream().map(s -> {
			try {
				return new String(s.getChaincodeActionResponsePayload());
			} catch (InvalidArgumentException e) {
				throw ExceptionUtil.wrapRuntime(e);
			}
		}).collect(Collectors.toList()));

	}

}